package cn.gs.serializable.old;

// @author gs
import cn.gs.reflect.ProxyTool;
import cn.gs.util.stream.TokenReader;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

public class SerialMap implements ISerialEntity {

    Map<ISerialEntity, ISerialEntity> map;

    @Override
    public <T> T deserialize(Class<T> clazz) {
        if (Map.class.isAssignableFrom(clazz)) {
            Map m = new HashMap();
            for (Entry<ISerialEntity, ISerialEntity> e : map.entrySet()) {
                m.put(e.getKey().deserialize(), e.getValue().deserialize());
            }
            try {
                T ins = clazz.newInstance();
                ((Map) ins).putAll(m);
                return ins;
            } catch (Throwable ex) {
            }
            try {
                for (Constructor c : clazz.getConstructors()) {
                    if (c.getParameterTypes().length == 1 && Map.class.isAssignableFrom(c.getParameterTypes()[0])) {
                        T ins = (T) c.newInstance(m);
                        return ins;
                    }
                }
            } catch (Throwable ex1) {
            }
            try {
                return ProxyTool.create(map, clazz);
            } catch (Throwable ex) {
            }
        } else {
            Map<String, ISerialEntity> m = new HashMap<String, ISerialEntity>();
            for (Entry<ISerialEntity, ISerialEntity> e : map.entrySet()) {
                Object k = e.getKey().deserialize();
                m.put(k == null ? null : k.toString(), e.getValue());
            }
            BeanInfo infos;
            try {
                infos = Introspector.getBeanInfo(clazz, clazz.getSuperclass());
                T ins = clazz.newInstance();
                for (PropertyDescriptor pd : infos.getPropertyDescriptors()) {
                    ISerialEntity v = m.get(pd.getDisplayName());
                    if (v != null && pd.getWriteMethod() != null && pd.getWriteMethod().getParameterTypes().length > 0) {
                        Object vv = v.deserialize(pd.getWriteMethod().getParameterTypes()[0]);
                        pd.getWriteMethod().invoke(ins, vv);
                    }
                }
                return ins;
            } catch (IllegalArgumentException ex) {
                Logger.getLogger(SerialMap.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InvocationTargetException ex) {
                Logger.getLogger(SerialMap.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IntrospectionException ex) {
                Logger.getLogger(SerialMap.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InstantiationException ex) {
                Logger.getLogger(SerialMap.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalAccessException ex) {
                Logger.getLogger(SerialMap.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return null;
    }

    @Override
    public Object deserialize() {
        return deserialize(Map.class);
    }

    @Override
    public boolean serialize(Object o) {
        if (o == null) {
            return false;
        }
        if (o instanceof Map) {
            Map c = (Map) o;
            map = new HashMap<ISerialEntity, ISerialEntity>();
            for (Iterator it = c.entrySet().iterator(); it.hasNext();) {
                Entry e = (Entry) it.next();
                ISerialEntity k = SerializableTool.serialize(e.getKey());
                ISerialEntity v = SerializableTool.serialize(e.getValue());
                map.put(k, v);
            }
            return true;
        } else {
            map = new HashMap<ISerialEntity, ISerialEntity>();
            BeanInfo infos;
            try {
                infos = Introspector.getBeanInfo(o.getClass(), o.getClass().getSuperclass());
                for (PropertyDescriptor pd : infos.getPropertyDescriptors()) {
                    try {
                        if (pd.getReadMethod() != null) {
                            Object f = pd.getReadMethod().invoke(o);
                            ISerialEntity v = SerializableTool.serialize(f);
                            SerialString k = new SerialString();
                            k.serialize(pd.getDisplayName());
                            map.put(k, v);
                        }
                    } catch (IllegalAccessException ex) {
                        Logger.getLogger(SerializableTool.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (IllegalArgumentException ex) {
                        Logger.getLogger(SerializableTool.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (InvocationTargetException ex) {
                        Logger.getLogger(SerializableTool.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            } catch (IntrospectionException ex) {
                Logger.getLogger(SerializableTool.class.getName()).log(Level.SEVERE, null, ex);
                return false;
            }
            return true;
        }
    }

    @Override
    public void write(Writer w) throws IOException {
        boolean b = false;
        w.write("{");
        for (Entry<ISerialEntity, ISerialEntity> i : map.entrySet()) {
            if (b) {
                w.write(",");
            } else {
                b = true;
            }
            i.getKey().write(w);
            w.write(":");
            i.getValue().write(w);
        }
        w.write("}");
    }

    @Override
    public boolean read(TokenReader reader) throws IOException, ClassNotFoundException {
        String c = reader.get();
        if (!"{".equals(c.trim())) {
            reader.back();
            return false;
        }
        map = new HashMap<ISerialEntity, ISerialEntity>();
        do {
            ISerialEntity k = SerializableTool.read(reader);
            if (k != null) {
                c = reader.get();
                if (!":".equals(c.trim())) {
                    reader.back();
                    return false;
                }
                ISerialEntity v = SerializableTool.read(reader);
                if (v != null) {
                    map.put(k, v);
                } else {
                    return false;
                }
                c = reader.get();
                if (!",".equals(c.trim())) {
                    reader.back();
                    break;
                }
            } else {
                break;
            }
        } while (true);
        c = reader.get();
        if (!"}".equals(c.trim())) {
            reader.back();
            return false;
        }
        return true;
    }

    @Override
    public String toString(String prefix) {
        StringWriter w = new StringWriter();
        w.write("[\n");
        for (Entry<ISerialEntity, ISerialEntity> i : map.entrySet()) {
            String s = i.getKey().toString(prefix + " ");
            w.write(s);
            w.write(":");
            s = i.getValue().toString("");
            w.write(s);
            w.write("\n");
        }
        w.write(prefix);
        w.write("]");
        return w.toString();
    }

    @Override
    public String toString() {
        return toString("");
    }
}
